<?php
// +----------------------------------------------------------------------
// | KITEGO-Admin「开箱即用」「人人全栈」
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2024 https://www.kitego.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed KITEGO并不是自由软件，未经许可不能去掉KITEGO相关版权
// +----------------------------------------------------------------------
// | Author: KITEGO Team <bd@kitego.cn>
// +----------------------------------------------------------------------

// 应用公共文件

use app\adminapi\services\setting\SystemConfigService;
use app\common\services\TranslationService;
use kitego\enums\ApiCodeEnum;
use think\contract\Arrayable;
use think\contract\Jsonable;
use think\helper\Str;
use think\Model;
use think\model\Collection;
use voku\helper\AntiXSS;

if (!function_exists('parse_amis_page')) {
    /**
     * 将TP分页数据解析成amis所需格式
     */
    function parse_amis_page($data)
    {
        $data['items'] = $data['data'];
        $data['page'] = $data['current_page'];
        unset($data['data'], $data['current_page'], $data['last_page'], $data['per_page']);

        return $data;
    }
}

if (!function_exists('amis_success')) {
    /**
     * 返回amis数据格式
     */
    function amis_success(mixed $data = [])
    {
        // amis编辑器直接返回data
        $data = !empty(req_decode()['amis']) ? $data : ['data' => $data];

        return success($data);
    }
}

if (!function_exists('success')) {
    /**
     * 请求成功
     * @param mixed $msg
     * @param array $data
     * @param int $code
     */
    function success(mixed $msg = 'ok', array $data = [], int $code = ApiCodeEnum::OK)
    {
        // 返回数据
        if ($msg instanceof Collection) {
            $msg = $msg->toArray();
        }
        if (is_object($msg)) {
            $msg = to_array($msg);
        }
        if (is_array($msg)) {
            $data = $msg;
            $msg = 'ok';
        }

        // 匹配语言包
        if (is_string($msg)) {
            $msg = get_lang($msg);
        }

        // 统一驼峰
        if (!empty($data)) {
            $data = convert_name_to_camel($data);
        }

        // 正式服加密
        if (config('aes.active') && !empty($data)) {
            $time = time();
            $data = [
                't' => $time,
                'k' => req_encode($data, $time)
            ];
        }

        return json(['status' => $code, 'data' => $data, 'message' => $msg]);
    }
}

if (!function_exists('fail')) {
    /**
     * 请求失败
     * @param mixed $msg
     * @param int $code
     * @param array $data
     */
    function fail(mixed $msg = 'fail', int $code = ApiCodeEnum::FAIL, array $data = [])
    {
        // 返回数据
        if ($msg instanceof Collection) {
            $msg = $msg->toArray();
        }
        if (is_object($msg)) {
            $msg = to_array($msg);
        }
        if (is_array($msg)) {
            $data = $msg;
            $msg = 'fail';
        }

        // 匹配语言包
        if (is_string($msg)) {
            $msg = get_lang($msg);
        }

        // 统一驼峰
        if (!empty($data)) {
            $data = convert_name_to_camel($data);
        }

        // 正式服加密
        if (config('aes.active') && !empty($data)) {
            $time = time();
            $data = [
                't' => $time,
                'k' => req_encode($data, $time)
            ];
        }

        return json(['status' => $code, 'data' => $data, 'message' => $msg]);
    }
}

if (!function_exists('req_encode')) {
    /**
     * 请求参数加密
     * @param array $data
     * @param string $time
     * @return false|string
     */
    function req_encode(array $data, string $time): bool|string
    {
        $aesConfig = config('aes');
        $key = $time . $aesConfig['key'];

        return openssl_encrypt(json_encode($data), "AES-128-CBC", $key, 0, $aesConfig['iv']);
    }
}

if (!function_exists('req_decode')) {
    /**
     * 请求参数解密
     * @throws Exception
     */
    function req_decode()
    {
        $params = request()->param();
        if (!empty($params) && config('aes.active')) {
            if (empty($params['t']) || !is_numeric($params['t']) || empty($params['k'])) {
                exception('k,t error');
            }

            $aesConfig = config('aes');
            $key = $params['t'] . $aesConfig['key'];
            $data = openssl_decrypt($params['k'], "AES-128-CBC", $key, 0, $aesConfig['iv']);

            return json_decode($data, true);
        }

        if (!isset($params['page'])) $params['page'] = 1;
        if (!isset($params['pageSize'])) $params['pageSize'] = $params['perPage'] ?? 10;

        return $params;
    }
}

if (!function_exists('to_array')) {
    /**
     * 把给定的值转化为数组
     */
    function to_array($value, $filter = false): mixed
    {
        if ($value === null || $value === '' || $value === []) {
            return [];
        }

        if ($value instanceof \Closure) {
            $value = $value();
        }

        if (is_array($value)) {
        } else if ($value instanceof Jsonable) {
            $value = json_decode($value->toJson(), true);
        } else if ($value instanceof Arrayable) {
            $value = $value->toArray();
        } else if (is_string($value)) {
            $array = null;

            try {
                $array = json_decode($value, true);
            } catch (\Throwable $e) {
            }

            $value = is_array($array) ? $array : explode(',', $value);
        } else {
            $value = (array)$value;
        }

        return $filter ? array_filter($value, function ($v) {
            return $v !== '' && $v !== null;
        }) : $value;
    }
}

if (!function_exists('convert_name_to_camel')) {
    /**
     * 键名统一驼峰
     * @param array $array
     * @return array
     */
    function convert_name_to_camel(array $array): array
    {
        $newArray = [];

        foreach ($array as $key => $value) {
            // 转换键名为驼峰
            $newKey = $key;
            if (str_contains($newKey, '_')) {
                $newKey = Str::camel($key);
            }

            if (is_array($value)) {
                // 如果是数组，则递归处理
                $newArray[$newKey] = convert_name_to_camel($value);
            } else {
                $newArray[$newKey] = $value;
            }
        }

        return $newArray;
    }
}

if (!function_exists('camel_to_snake')) {
    /**
     * 驼峰转下划线
     * @param $camelCase
     * @return string
     */
    function camel_to_snake($camelCase): string
    {
        // 替换非字母数字字符（可选，取决于你的具体需求）
        $camelCase = preg_replace('/[^a-zA-Z0-9\x7f-\xff]+/', '_', $camelCase);

        // 将驼峰命名转换为下划线命名
        return strtolower(preg_replace('/([a-z\d])([A-Z])/', '$1_$2', $camelCase));
    }
}

if (!function_exists('get_lang')) {
    /**
     * 多语言
     * @param $message
     * @param array $replace
     * @return string
     */
    function get_lang($message, array $replace = []): string
    {
        try {
            // 语言包
            $langTrans = app()->make(TranslationService::class)->listTranslationJson('api');
            $message = $langTrans[$message];

            // 替换变量
            if (!empty($replace) && is_array($replace)) {
                // 关联索引解析
                $key = array_keys($replace);
                foreach ($key as &$v) {
                    $v = "{:$v}";
                }
                $message = str_replace($key, $replace, $message);
            }

            return $message;
        } catch (Exception $e) {
            kg_file_put_contents('getLang', [$e->getMessage() . '|' . $e->getFile() . '|' . $e->getLine()]);
            return $message;
        }
    }
}

if (!function_exists('now')) {
    /**
     * 当前时分秒
     */
    function now(): string
    {
        return date('Y-m-d H:i:s');
    }
}

if (!function_exists('array_to_object')) {
    /**
     * 数组转对象
     * @param $array
     * @return mixed|stdClass
     */
    function array_to_object($array): mixed
    {
        if (is_array($array)) {
            $object = new stdClass();
            foreach ($array as $key => $value) {
                $object->$key = array_to_object($value);
            }
            return $object;
        } else {
            return $array;
        }
    }
}

if (!function_exists('kg_file_put_contents')) {
    /**
     * 打印日志
     * @param string $logName eg:test
     * @param array $msg eg:['first msg', 'second msg']
     */
    function kg_file_put_contents(string $logName, array $msg): void
    {
        $date = date('Ymd', time());
        $info = '时间 ' . now() . ' ';

        foreach ($msg as $v) {
            $info .= ($v . PHP_EOL);
        }

        $info .= PHP_EOL;
        $nestedDir = runtime_path() . DIRECTORY_SEPARATOR . "kg_file_put_contents" . DIRECTORY_SEPARATOR . $date . DIRECTORY_SEPARATOR . $logName . DIRECTORY_SEPARATOR;
        $logFile = date('Y-m-d', time()) . '.log';

        $logService = new \kitego\services\common\LogService($nestedDir, $logFile);
        $logService->log($info);
    }
}

if (!function_exists('make_tree')) {
    /**
     * 生成子孙树
     * @param array $data 数据
     * @param string $childName 子数据名
     * @param string $idName 主键key名称
     * @param string $pidName 数据上级key名
     * @return array
     */
    function make_tree(array $data, string $childName = 'children', string $idName = 'id', string $pidName = 'pid'): array
    {
        if (empty($data)) return [];

        $list = [];
        foreach ($data as $v) {
            $list[$v[$idName]] = $v;
        }

        $tree = [];
        foreach ($list as $v) {
            if (isset($list[$v[$pidName]])) {
                $list[$v[$pidName]][$childName][] = &$list[$v[$idName]];
            } else {
                $tree[] = &$list[$v[$idName]];
            }
        }

        return $tree;
    }
}

if (!function_exists('find_array_value_by_key')) {
    /**
     * 多维数组通过键名查找键值
     * @param string $key
     * @param array $array
     * @return array
     */
    function find_array_value_by_key(string $key, array $array): array
    {
        $results = [];
        foreach ($array as $k => $v) {
            if (is_array($v)) {
                // 如果是多维数组，递归调用
                $results = array_merge($results, find_array_value_by_key($key, $v));
            } elseif ($k == $key) {
                // 找到指定的key，将其value添加到结果数组中
                if (empty($v)) {
                    continue;
                }
                $results[] = $v;
            }
        }
        return $results;
    }
}

if (!function_exists('exception')) {
    /**
     * 执行业务抛出异常
     * @param $message
     */
    function exception($message)
    {
        throw new \kitego\exception\KitegoBaseException($message);
    }
}

if (!function_exists('sys_config')) {
    /**
     * 读取系统配置函数
     * @param string|null $groupName
     * @param array|null $keys
     * @return array|mixed
     */
    function sys_config(?string $groupName = null, ?array $keys = null): mixed
    {
        $res = app()->make(SystemConfigService::class)->listSystemConfig($groupName, $keys, 1);

        return $res->getData()['data'] ?? [];
    }
}

if (!function_exists('ms_time')) {
    /**
     * 获取毫秒数
     * @return float
     */
    function ms_time()
    {
        list($ms, $sec) = explode(' ', microtime());
        return (float)sprintf('%.0f', (floatval($ms) + floatval($sec)) * 1000);
    }
}

if (!function_exists('blank')) {
    /**
     * 给定的值是否为空白
     * @return bool
     */
    function blank($value)
    {
        if (is_null($value)) {
            return true;
        }

        if (is_string($value)) {
            return trim($value) === '';
        }

        if (is_numeric($value) || is_bool($value)) {
            return false;
        }

        if ($value instanceof Model) {
            return false;
        }

        if ($value instanceof Countable) {
            return count($value) === 0;
        }

        if ($value instanceof Stringable) {
            return trim((string)$value) === '';
        }

        return empty($value);
    }
}

if (!function_exists('check_url')) {
    /**
     * 校验域名格式：http(s)://www.kitego.cn
     */
    function check_url($url)
    {
        // 检查是否以 http:// 开头
        if (!str_starts_with($url, 'http://') && !str_starts_with($url, 'https://')) {
            return false;
        }

        // 检查是否为合法URL
        if (!filter_var($url, FILTER_VALIDATE_URL)) {
            return false;
        }

        // 检查末尾是否有 /
        if (str_ends_with($url, '/')) {
            return false;
        }

        return true;
    }
}

if (!function_exists('html')) {
    /**
     * HTML 特殊字符进行转义，防止 XSS 攻击
     */
    function html_special_chars($string)
    {
        $string = trim($string);

        // 过滤html和php标签
        $string = strip_tags($string);

        return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, 'UTF-8');
    }
}

if (!function_exists('html_special_chars_decode')) {
    /**
     * 完全解码 html_special_chars 编码的字符串
     */
    function html_special_chars_decode(string $string, int $flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401): string
    {
        return htmlspecialchars_decode($string, $flags);
    }
}

if (!function_exists('xss_clean')) {

    /**
     * 清理XSS，通常只用于富文本
     */
    function xss_clean(string $string)
    {
        return (new AntiXSS())->xss_clean($string);
    }
}

if (!function_exists('is_xss_found')) {

    /**
     * 清理XSS，通常只用于富文本
     */
    function is_xss_found(string $string)
    {
        $antiXss = new AntiXSS();
        $harmless_string = $antiXss->xss_clean($string);

        return [
            'origin' => $string,
            'clean' => $harmless_string,
            'isXssFound' => $antiXss->isXssFound(),
        ];
    }
}


